package com.cehome.cloudbox.service.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cehome.cloudbox.constans.Constants;
import com.cehome.cloudbox.constans.ExtensionTaskProperties;
import com.cehome.cloudbox.constans.MsgConstants;
import com.cehome.cloudbox.doman.dto.*;
import com.cehome.cloudbox.doman.vo.ProcTaskPageQueryVo;
import com.cehome.cloudbox.enums.ProcCommentTypeEnum;
import com.cehome.cloudbox.exception.BizException;
import com.cehome.cloudbox.service.IProcCommonService;
import com.cehome.cloudbox.service.IProcTaskService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 任务 服务实现类
 * </p>
 */
@Service
@Slf4j
public class ProcTaskServiceImpl implements IProcTaskService {
    @Resource
    private IProcCommonService iProcCommonService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private ProcInstanceServiceImpl procInstanceService;

    /**
     * 流程审批同意
     *
     * @param param 流程任务处理DTO
     * @return 流程状态DTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProcStateDto agree(ProcTaskHandleDto param) {
        Task task = taskService.createTaskQuery().active().taskId(param.getTaskId()).singleResult();
        if (ObjectUtil.isEmpty(task)) {
            log.error("任务取得失败，任务ID：{}", param.getTaskId());
            throw new BizException(MsgConstants.ERR_E099);
        }
        String userId = "11111";

        // 申请节点
        String applyDefId = taskService.getVariable(task.getId(), ExtensionTaskProperties.FLOWABLE_APPLY_DEFINITION_ID, String.class);
        if (task.getTaskDefinitionKey().equals(applyDefId)) {
            // 添加申请意见
            iProcCommonService.addTaskHandleComment(task.getId(), task.getProcessInstanceId(), ProcCommentTypeEnum.APPLY.getType(),
                    param.getComment(), userId);
        } else {
            // 添加审批意见
            iProcCommonService.addTaskHandleComment(task.getId(), task.getProcessInstanceId(), ProcCommentTypeEnum.AGREE.getType(),
                    param.getComment(), userId);
        }

        if (DelegationState.PENDING.equals(task.getDelegationState())) {
            // 执行委托任务
            taskService.resolveTask(task.getId(), param.getVariables());
        } else {
            // 修改执行人 其实我这里就相当于领取了
            taskService.setAssignee(task.getId(), userId);
            taskService.complete(task.getId(), param.getVariables());
        }
        return iProcCommonService.procIncState(task.getProcessInstanceId());
    }

    /**
     * 流程审批拒绝
     *
     * @param param 流程任务处理DTO
     * @return 流程状态DTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProcStateDto refuse(ProcTaskHandleDto param) {
        Task task = taskService.createTaskQuery().active().taskId(param.getTaskId()).singleResult();
        if (ObjectUtil.isEmpty(task)) {
            log.error("任务取得失败，任务ID：{}", param.getTaskId());
            throw new BizException(MsgConstants.ERR_E099);
        }

        // 添加驳回意见
        String userId = "11111";
        iProcCommonService.addTaskHandleComment(task.getId(), task.getProcessInstanceId(), ProcCommentTypeEnum.REJECT.getType(),
                param.getComment(), userId);
        // 设置审批人
        taskService.setAssignee(task.getId(), userId);
        // 获取bpmn对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        // 获取节点信息
        FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
        // 获取驳回节点
        String targetDefId = flowElement.getAttributeValue(ExtensionTaskProperties.NAMESPACE_URI, ExtensionTaskProperties.REJECT_TASK_NODE_ID);
        // 如果驳回节点为空则使用申请节点
        if (StrUtil.isEmpty(targetDefId)) {
            targetDefId = taskService.getVariable(task.getId(), ExtensionTaskProperties.FLOWABLE_APPLY_DEFINITION_ID, String.class);
        }
        // 普通驳回节点
        List<Execution> executions = runtimeService.createExecutionQuery()
                .parentId(task.getProcessInstanceId())
                .list();
        List<String> executionIds = executions.stream().map(Execution::getId).collect(Collectors.toList());
        // 执行跳转
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(task.getProcessInstanceId())
                .moveExecutionsToSingleActivityId(executionIds, targetDefId)
                .changeState();
        return iProcCommonService.procIncState(task.getProcessInstanceId());

    }

    /**
     * 流程转办
     *
     * @param param 流程转办DTO
     * @return 流程状态DTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProcStateDto assign(ProcAssignDto param) {
        Task task = taskService.createTaskQuery().active().taskId(param.getTaskId()).singleResult();
        if (ObjectUtil.isEmpty(task)) {
            log.error("任务取得失败，任务ID：{}", param.getTaskId());
            throw new BizException(MsgConstants.ERR_E099);
        }

        String userId = "11111";
        // 添加转办意见
        iProcCommonService.addTaskHandleComment(task.getId(), task.getProcessInstanceId(), ProcCommentTypeEnum.ASSIGN.getType(),
                param.getComment(), userId);
        // 设置审批人
        taskService.setAssignee(task.getId(), param.getAssignUserId());
        return iProcCommonService.procIncState(task.getProcessInstanceId());
    }

    @Override
    public ProcStateDto rejectAndTermination(ProcTaskHandleDto param) {
        this.refuse(param);
        ProcInsHandleDto procInsHandleDto = new ProcInsHandleDto();
        procInsHandleDto.setProcInsId(param.getProcInsId());
        procInsHandleDto.setComment(param.getComment());
        return procInstanceService.cancel(procInsHandleDto);
    }

    /**
     * 待办任务列表
     *
     * @param param 流程任务分页查询VO
     * @return 待办分页列表
     */
    @Override
    public Page<ProcTaskPageShowVo> todoList(ProcTaskPageQueryVo param, Boolean isPage) {
        // 根据候选人查询
        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .or()
                .taskAssignee(param.getUserId())
                .taskCandidateUser(param.getUserId())
                .endOr()
                .orderByTaskCreateTime()
                .desc();
        // 流程编码
        if (StrUtil.isNotEmpty(param.getProcDefCode())) {
            taskQuery.processDefinitionKey(param.getProcDefCode());
        }
        // 流水单号
        if (StrUtil.isNotEmpty(param.getProcSerialNo())) {
            taskQuery.processVariableValueLikeIgnoreCase(ExtensionTaskProperties.FLOWABLE_PROCESS_SERIAL_NO,
                    Constants.WILDCARD + param.getProcSerialNo() + Constants.WILDCARD);
        }
        Page<ProcTaskPageShowVo> page = new Page<>(param.getCurrent(), param.getPageSize(), taskQuery.count());
        List<Task> taskList = taskQuery.listPage((param.getCurrent() - 1)*param.getPageSize(), param.getPageSize());
        if (ObjectUtil.isEmpty(taskList)) {
            return page;
        }

        // 流程定义信息
        List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery()
                .processDefinitionIds(taskList.stream().map(TaskInfo::getProcessDefinitionId).collect(Collectors.toSet()))
                .list();

        // 获取流程编码对应流程类型Map
        List<String> procDefCodeList = processDefinitionList.stream().map(ProcessDefinition::getKey).distinct().collect(Collectors.toList());

        // 获取发起人信息
        Set<String> procInsIdSet = taskList.stream().map(TaskInfo::getProcessInstanceId).collect(Collectors.toSet());
        List<HistoricProcessInstance> hisProcInsList = historyService.createHistoricProcessInstanceQuery()
                .processInstanceIds(procInsIdSet)
                .list();

        // 获取人员信息
//        Map<String, String> userMap = getUserMap(hisProcInsList.stream().map(HistoricProcessInstance::getStartUserId).collect(Collectors.toList()));

        // 编辑
        List<ProcTaskPageShowVo> procTaskPageShowVoList = new ArrayList<>();
        ProcTaskPageShowVo procTaskPageShowVo;
        List<ProcessDefinition> processDefinitionCollect;
        List<HistoricProcessInstance> historicProcessInstanceCollect;
        for (Task task : taskList) {
            procTaskPageShowVo = new ProcTaskPageShowVo();
            // 流程定义相关信息
            procTaskPageShowVo.setProcDefId(task.getProcessDefinitionId());
            processDefinitionCollect = processDefinitionList.stream()
                    .filter(proc -> task.getProcessDefinitionId().equals(proc.getId())).collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(processDefinitionCollect)) {
                procTaskPageShowVo.setProcDefCode(processDefinitionCollect.get(0).getKey());
                procTaskPageShowVo.setProcDefName(processDefinitionCollect.get(0).getName());
            }

            // 流程实例相关信息
            procTaskPageShowVo.setProcInsId(task.getProcessInstanceId());

            // 设定流水单号
            procTaskPageShowVo.setProcSerialNo(Convert.toStr(task.getProcessVariables().get(ExtensionTaskProperties.FLOWABLE_PROCESS_SERIAL_NO)));

            // 设定发起人等信息
            historicProcessInstanceCollect = hisProcInsList.stream()
                    .filter(his -> task.getProcessInstanceId().equals(his.getId())).collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(historicProcessInstanceCollect)) {
                if (historicProcessInstanceCollect.get(0).getStartUserId().equals(param.getUserId())) {
                    continue;
                }
                procTaskPageShowVo.setBusId(historicProcessInstanceCollect.get(0).getBusinessKey());
                procTaskPageShowVo.setStartUserId(historicProcessInstanceCollect.get(0).getStartUserId());
//                if (!StringUtils.isEmpty(historicProcessInstanceCollect.get(0).getStartUserId()) && isPage){
//                    R<SysOrganization> sysOrganizationR = iOrgFeign.orgByUser(Long.valueOf(historicProcessInstanceCollect.get(0).getStartUserId()));
//                    if (sysOrganizationR != null) {
//                        procTaskPageShowVo.setUserUnitName(sysOrganizationR.getData().getName());
//                    }
//                }
//                procTaskPageShowVo.setStartUserName(userMap.get(historicProcessInstanceCollect.get(0).getStartUserId()));
                procTaskPageShowVo.setStartTime(historicProcessInstanceCollect.get(0).getStartTime());
            }

            // 任务相关信息
            procTaskPageShowVo.setTaskId(task.getId());
            procTaskPageShowVo.setTaskName(task.getName());
            // 流程申请节点ID
            String applyDefId = Convert.toStr(task.getProcessVariables().get(ExtensionTaskProperties.FLOWABLE_APPLY_DEFINITION_ID));
            if(StringUtils.isNotEmpty(applyDefId)){
                // 标识数据是否可申请
                procTaskPageShowVo.setApplyFlg(applyDefId.equals(task.getTaskDefinitionKey()));
            }else{
                procTaskPageShowVo.setApplyFlg(false);
            }
            procTaskPageShowVoList.add(procTaskPageShowVo);
        }

        page.setRecords(procTaskPageShowVoList);
        return page;
    }

    /**
     * 已办任务列表
     *
     * @param param 流程任务分页查询VO
     * @return 已办任务列表
     */
    @Override
    public Page<ProcTaskPageShowVo> doneList(ProcTaskPageQueryVo param) {
        // 根据候选人查询
        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .includeProcessVariables()
                .finished()
                .taskAssignee(param.getUserId())
                .orderByHistoricTaskInstanceEndTime()
                .desc();

        // 流程编码
        if (StrUtil.isNotEmpty(param.getProcDefCode())) {
            historicTaskInstanceQuery.processDefinitionKey(param.getProcDefCode());
        }
        // 流水单号
        if (StrUtil.isNotEmpty(param.getProcSerialNo())) {
            historicTaskInstanceQuery.processVariableValueLikeIgnoreCase(ExtensionTaskProperties.FLOWABLE_PROCESS_SERIAL_NO,
                    Constants.WILDCARD + param.getProcSerialNo() + Constants.WILDCARD);
        }
        Page<ProcTaskPageShowVo> page = new Page<>(param.getCurrent(), param.getPageSize(), historicTaskInstanceQuery.count());
        List<HistoricTaskInstance> hisTaskInsList = historicTaskInstanceQuery.listPage((param.getCurrent() - 1)*param.getPageSize(), param.getPageSize());
        if (ObjectUtil.isEmpty(hisTaskInsList)) {
            return page;
        }

        // 流程定义信息
        List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery()
                .processDefinitionIds(hisTaskInsList.stream().map(TaskInfo::getProcessDefinitionId).collect(Collectors.toSet()))
                .list();

        // 获取流程编码对应流程类型Map
        List<String> procDefCodeList = processDefinitionList.stream().map(ProcessDefinition::getKey).distinct().collect(Collectors.toList());

        // 获取发起人信息
        Set<String> procInsIdSet = hisTaskInsList.stream().map(TaskInfo::getProcessInstanceId).collect(Collectors.toSet());
        List<HistoricProcessInstance> hisProcInsList = historyService.createHistoricProcessInstanceQuery()
                .processInstanceIds(procInsIdSet)
                .list();

        // 获取人员信息
//        Map<String, String> userMap = getUserMap(
//                hisProcInsList.stream().map(HistoricProcessInstance::getStartUserId).distinct().collect(Collectors.toList()));

        // 编辑
        List<ProcTaskPageShowVo> procTaskPageShowVoList = new ArrayList<>();
        ProcTaskPageShowVo procTaskPageShowVo;
        List<ProcessDefinition> processDefinitionCollect;
        List<HistoricProcessInstance> historicProcessInstanceCollect;
        for (HistoricTaskInstance hisTaskIns : hisTaskInsList) {
            procTaskPageShowVo = new ProcTaskPageShowVo();
            // 流程定义相关信息
            procTaskPageShowVo.setProcDefId(hisTaskIns.getProcessDefinitionId());
            processDefinitionCollect = processDefinitionList.stream()
                    .filter(proc -> hisTaskIns.getProcessDefinitionId().equals(proc.getId())).collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(processDefinitionCollect)) {
                procTaskPageShowVo.setProcDefName(processDefinitionCollect.get(0).getName());
                procTaskPageShowVo.setProcDefCode(processDefinitionCollect.get(0).getKey());
            }

            // 流程实例相关信息
            procTaskPageShowVo.setProcInsId(hisTaskIns.getProcessInstanceId());
            // 设定流水单号
            procTaskPageShowVo.setProcSerialNo(Convert.toStr(hisTaskIns.getProcessVariables().get(ExtensionTaskProperties.FLOWABLE_PROCESS_SERIAL_NO)));
            // 设定发起人等信息
            historicProcessInstanceCollect = hisProcInsList.stream()
                    .filter(his -> hisTaskIns.getProcessInstanceId().equals(his.getId())).collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(historicProcessInstanceCollect)) {

                procTaskPageShowVo.setBusId(historicProcessInstanceCollect.get(0).getBusinessKey());
                procTaskPageShowVo.setStartUserId(historicProcessInstanceCollect.get(0).getStartUserId());
//                if (!StringUtils.isEmpty(historicProcessInstanceCollect.get(0).getStartUserId())){
//                    R<SysOrganization> sysOrganizationR = iOrgFeign.orgByUser(Long.valueOf(historicProcessInstanceCollect.get(0).getStartUserId()));
//                    procTaskPageShowVo.setUserUnitName(sysOrganizationR.getData().getName());
//                }
//                procTaskPageShowVo.setStartUserName(userMap.get(historicProcessInstanceCollect.get(0).getStartUserId()));
                procTaskPageShowVo.setStartTime(historicProcessInstanceCollect.get(0).getStartTime());
            }
            procTaskPageShowVo.setEndTime(hisTaskIns.getEndTime());

            // 任务相关信息
            procTaskPageShowVo.setTaskId(hisTaskIns.getId());
            procTaskPageShowVo.setTaskName(hisTaskIns.getName());
            // 流程申请节点ID
            String applyDefId = Convert.toStr(hisTaskIns.getProcessVariables().get(ExtensionTaskProperties.FLOWABLE_APPLY_DEFINITION_ID));
            // 标识数据是否可撤回
            procTaskPageShowVo.setRevokeFlg(getRevokeFlg(hisTaskIns.getProcessInstanceId(), hisTaskIns.getTaskDefinitionKey(), applyDefId,
                    hisTaskIns.getId()));
            procTaskPageShowVoList.add(procTaskPageShowVo);
        }

        page.setRecords(procTaskPageShowVoList);
        return page;
    }

    /**
     * 标识数据是否可撤回
     *
     * @param procInsId            流程定义ID
     * @param hisTaskDefinitionKey 当前历史节点ID
     * @param applyDefId           流程申请节点ID
     * @param hisTaskId            流程任务ID
     * @return true/false
     */
    private boolean getRevokeFlg(String procInsId, String hisTaskDefinitionKey, String applyDefId, String hisTaskId) {

        // 当前历史节点与申请节点ID不同时
        if (!hisTaskDefinitionKey.equals(applyDefId)) {
            return false;
        }

        // 获取上一节点任务
        HistoricTaskInstance hisTaskIns = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(procInsId)
                .finished()
                .orderByHistoricTaskInstanceEndTime()
                .desc()
                .list()
                .get(0);

        // 申请节点和上一节点节点相同 且 当前任务ID和上一任务ID相同
        return applyDefId.equals(hisTaskIns.getTaskDefinitionKey()) && hisTaskId.equals(hisTaskIns.getId());
    }

}
